﻿using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;

namespace NMemcached.Client
{
    /// <summary>
    /// Compresses and decompresses byte arrays using the GZip algorithm
    /// </summary>
    public class GZipCompressor : ICompressor
    {
        /// <summary>
        /// Initializes a new instance of the GZipCompressor class
        /// </summary>
        public GZipCompressor()
        {
        }

        /// <summary>
        /// Compresses a sequence of bytes using the GZip algorithm
        /// </summary>
        /// <param name="data">The sequence of bytes to compress</param>
        /// <returns>The compressed sequence of bytes</returns>
        public byte[] Compress(byte[] data)
        {
            if (data == null)
            {
                throw new ArgumentNullException("data");
            }

            using (MemoryStream compressed = new MemoryStream())
            {
                using (GZipStream compressionStream = new GZipStream(compressed, CompressionMode.Compress))
                {
                    compressionStream.Write(data, 0, data.Length);
                    compressionStream.Flush();
                    //Not calling closed leaves a vew bytes off the end of the output
                    compressionStream.Close();

                    byte[] compressedBytes = compressed.ToArray();
                    return compressedBytes;
                }
            }
        }

        /// <summary>
        /// Decompresses a sequence of bytes using the GZip algorithm
        /// </summary>
        /// <param name="data">The sequence of bytes to decompress</param>
        /// <returns>The decompressed sequence of bytes</returns>
        public byte[] Decompress(byte[] data)
        {
            if (data == null)
            {
                throw new ArgumentNullException("data");
            }

            using (MemoryStream inputStream = new MemoryStream(data), outputStream = new MemoryStream())
            {
                inputStream.Position = 0;
                using (GZipStream decompressionStream = new GZipStream(inputStream, CompressionMode.Decompress))
                {
                    byte[] buffer = new byte[1024];
                    int bytesRead = decompressionStream.Read(buffer, 0, buffer.Length);
                    if (bytesRead == 0)
                    {
                        //Grr, that compression stream bug is still around
                        //The first read only fills the internal buffer and
                        //outputs no results so try it one more time before
                        //giving up
                        bytesRead = decompressionStream.Read(buffer, 0, buffer.Length);
                    }
                    while (bytesRead > 0)
                    {
                        outputStream.Write(buffer, 0, bytesRead);
                        outputStream.Flush();
                        bytesRead = decompressionStream.Read(buffer, 0, buffer.Length);
                    }
                    byte[] outputBytes = outputStream.ToArray();
                    return outputBytes;
                }
            }
        }
    }
}
